package com.wzm.utils.lmdb;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.lmdbjava.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

import java.io.File;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;

import static java.lang.Integer.BYTES;
import static java.lang.System.getProperty;
import static java.nio.ByteBuffer.allocateDirect;
import static org.lmdbjava.ByteBufferProxy.PROXY_OPTIMAL;
import static org.lmdbjava.DbiFlags.MDB_CREATE;

/**
 * <p>lmdb操作工具类</p>
 *
 * @author wwb
 * @date 2021/6/25 10:22
 */
@Configuration
public class LmdbConfig {
    private static final Log logger = LogFactory.getLog(LmdbConfig.class);
    /*
     * 原本打算用日期生成存储文件,但是后来抛弃了这个操作
     * */
    @SuppressWarnings("unused")
    private static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd");
    /**
     * lmdb window地址
     */
    @Value("${lmdb.winLmdbPath}")
    private static String winLmdbPath;
    /**
     * lmdb linux地址
     */
    @Value("${lmdb.linLmdbPath}")
    private static String linLmdbPath;
    /**
     * lmdb Mac地址
     */
    @Value("/Users/zhaolixue/chj/db/lmdb")
    private static String osLmdbPath;

    private static String lmdbPath = "/Users/zhaolixue/Downloads";
    /**
     * env
     */
    private Env<ByteBuffer> env;

    /**
     * lmdb max size  单位字节,自己乘算变成MB使用了
     */
    private static final int lmdbMaxSize = 256;

    /**
     * lmdb库的数量
     */
    private static final int lmdbDatabaseCount = 10;

    /**
     * lmdb库最大读取连接数
     */
    private static final int lmdbDatabaseReader = 20;

    private static final String os = getProperty("os.name");


    public LmdbConfig(){
//        initPath();
        logger.info("init-------lmdb初始化开始。。。。。。。");
        if (lmdbPath == null || lmdbPath.equals(""))
        {
            logger.info("lmdbPath 地址未获取");
        } else
        {
            try
            {
                logger.info("FILEPATH：" + lmdbPath);
                File path = new File(lmdbPath);
                boolean flag = path.exists();
                if (!flag)  //Lmdb文件夹不存在
                {
                    flag = path.mkdirs();
                    logger.info("init-------Lmdb文件夹暂无，创建Lmdb文件夹状态：" + flag);
                } else
                {
                    logger.info("init-------Lmdb文件夹已存在，开始数据库建立");
                }
                if (flag)
                {
                    logger.info("init-----初始化Lmdb----.so文件初始化......");
                    env = org.lmdbjava.Env.create(PROXY_OPTIMAL)
                            .setMapSize(lmdbMaxSize * 1024 * 1024)
                            .setMaxDbs(lmdbDatabaseCount)
                            .setMaxReaders(lmdbDatabaseReader)
                            .open(path);
                } else
                {  //flag如果为false  文件夹生成失败，库建立失败，
                    logger.error("init-------Lmdb初始化文件生成异常，请检查文件夹权限");
                }
                logger.info("init-----初始化Lmdb结束......");
            } catch (Exception e)
            {
                logger.error("init-------lmdb初始化异常", e);
            }
        }
    }

    /**
     * Description: 默认初始化操作 <br>
     */
    @SuppressWarnings("used")
    public void lmdbInit()
    {
//        initPath();
        logger.info("init-------lmdb初始化开始。。。。。。。");
        if (lmdbPath == null || lmdbPath.equals(""))
        {
            logger.info("lmdbPath 地址未获取");
        } else
        {
            try
            {
                logger.info("FILEPATH：" + lmdbPath);
                File path = new File(lmdbPath);
                boolean flag = path.exists();
                if (!flag)  //Lmdb文件夹不存在
                {
                    flag = path.mkdirs();
                    logger.info("init-------Lmdb文件夹暂无，创建Lmdb文件夹状态：" + flag);
                } else
                {
                    logger.info("init-------Lmdb文件夹已存在，开始数据库建立");
                }
                if (flag)
                {
                    logger.info("init-----初始化Lmdb----.so文件初始化......");
                    env = org.lmdbjava.Env.create(PROXY_OPTIMAL)
                            .setMapSize(lmdbMaxSize * 1024 * 1024)
                            .setMaxDbs(lmdbDatabaseCount)
                            .setMaxReaders(lmdbDatabaseReader)
                            .open(path);
                } else
                {  //flag如果为false  文件夹生成失败，库建立失败，
                    logger.error("init-------Lmdb初始化文件生成异常，请检查文件夹权限");
                }
                logger.info("init-----初始化Lmdb结束......");
            } catch (Exception e)
            {
                logger.error("init-------lmdb初始化异常", e);
            }
        }
    }

    /**
     * Description: 向库中插入数据 <br>
     */
    @SuppressWarnings("unused")
    public void putValueToDb(String dbName, String key, String value)
    {
        logger.info("putValueToDb-----lmdb写入数据开始-----");
        Dbi<ByteBuffer> db = env.openDbi(dbName, MDB_CREATE);
        Txn<ByteBuffer> txn = env.txnWrite();
        Cursor<ByteBuffer> c;
        try
        {
            c = db.openCursor(txn);
            c.put(bb(key), bb(value));
            txn.commit();
            logger.info("putValueToDb-----lmdb写入数据结束-----");
        } catch (Exception e)
        {
            logger.error("putValueToDb------lmdb数据写入异常", e);
        } finally
        {
            close(null, txn);  //多次写入不关闭写入流，关闭会抛出异常
        }

    }

    /**
     * Description: 根据指定的key获取数据 <br>
     */
    @SuppressWarnings("unused")
    public String getValueByKey(String dbName, String key)
    {
        logger.info("getValueByKey-----lmdb获取数据开始-----");
        Dbi<ByteBuffer> db = env.openDbi(dbName, MDB_CREATE);
        Cursor<ByteBuffer> c = null;
        String value = "";
        Txn<ByteBuffer> txn = env.txnRead();
        try
        {
            c = db.openCursor(txn);
            c.get(bb(key), GetOp.MDB_SET_KEY);
            ByteBuffer byteBuffer = c.val();
            value = StandardCharsets.UTF_8.decode(byteBuffer).toString();
            logger.info("getValueByKey-----lmdb获取数据结束-----");
        } catch (Exception e)
        {
            logger.error("getValueByKey-------lmdb数据读取异常", e);
        } finally
        {
            close(c, txn);
        }
        return value;
    }

    /**
     * Description:  事务连接释放操作封装
     */
    public void close(Cursor<ByteBuffer> c, Txn<ByteBuffer> txn)
    {
        logger.info("close--------本次链接关闭");
        try
        {
            if (c != null)
            {
                c.close();
            }
            if (txn != null)
            {
                txn.close();
            }
        } catch (Exception e)
        {
            logger.error("close--------连接关闭异常", e);
        }
    }

    /**
     * Description: 获取库下所有的数据 <br>
     */
    public Map<String, String> getAllValueByDbName(String dbName)
    {
        logger.info("getAllValueByDbName-----lmdb获取数据开始-----");
        Dbi<ByteBuffer> db = env.openDbi(dbName, MDB_CREATE);
        Map<String, String> map = new HashMap<>();
        Txn<ByteBuffer> txn = env.txnRead();
        Cursor<ByteBuffer> cursor = null;
        try
        {
            cursor = db.openCursor(txn);
            while (cursor.next())
            {
                ByteBuffer key = cursor.key();
                ByteBuffer val = cursor.val();
                byte[] k = new byte[key.capacity()];
                byte[] v = new byte[val.capacity()];
                key.get(k);
                val.get(v);
                map.put(new String(k, StandardCharsets.UTF_8), new String(v, StandardCharsets.UTF_8));
            }
            logger.info("getAllValueByDbName-----lmdb获取数据结束-----");
        } catch (Exception e)
        {
            logger.error("lmdb读取全部数据异常", e);
        } finally
        {
            close(cursor, txn);
        }
        return map;
    }

    /**
     * Description: 根据dbname获取该库下的数量 <br>
     */
    @SuppressWarnings("unused")
    public long getDbCount(String dbName)
    {
        logger.info("getDbCount-----------lmdb获取数量开始");
        long count = 0;
        try
        {
            count = getAllValueByDbName(dbName).size();
            logger.info("getDbCount---------lmdb获取数量结束");
        } catch (Exception e)
        {
            logger.error("getDbCount----------lmdb获取数量异常");
        }
        return count;
    }

    /**
     * 格式化 ByteBuffer
     */
    @SuppressWarnings("unused")
    static ByteBuffer bb(final int value)
    {
        ByteBuffer bb = allocateDirect(BYTES);
        bb.putInt(value).flip();
        return bb;
    }

    /**
     * 格式化 ByteBuffer
     */
    @SuppressWarnings("unused")
    static ByteBuffer bb(final String value)
    {
        byte[] val = value.getBytes(StandardCharsets.UTF_8);
        final ByteBuffer bb = allocateDirect(val.length);
        bb.put(val).flip();
        return bb;
    }


    /**
     * 懒人封装
     */
    private void initPath(){
        if (os.matches("^(?i)Linux.*$"))
        {
            lmdbPath = linLmdbPath;
        } else if (os.startsWith("Mac OS X"))
        {
            lmdbPath = winLmdbPath;
        } else if (os.startsWith("Windows"))
        {
            lmdbPath = osLmdbPath;
        }
    }
}

